#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <map>
#include <set>
using std::cout;
using std::cerr;
using std::endl;
using std::string;
using std::ifstream;
using std::ofstream;
using std::istringstream;
using std::map;
using std::set;
using std::vector;
using std::cout;
using std::endl;

class TextQuery
{
public:
    //......
    void readFile(const string filename);
    void query(const string & word);//

private:
    //vector存储每一行的内容，
    //等查找到单词，需要输出所在行内容
    vector<string> _lines;

    //存储单词和它出现的行号
    map<string, set<int> > _wordNumbers;
    //存储单词和它出现的次数
    map<string, int> _dict;
};


void TextQuery::readFile(const string filename){
    ifstream ifs(filename);
    if(!ifs.good()){
        cerr << "fail to open file" << endl;
        return;
    }

    int lineNumber = 0;
    string line;
    while(getline(ifs,line)){
        ++lineNumber;
        //vector存储一行的内容
        _lines.push_back(line);

        istringstream iss(line);
        string word;
        while(iss >> word){
            //处理数字问题
            bool hasDigit = false;
            for(auto & ch : word){
                if(isdigit(ch)){
                    hasDigit = true;
                    break;
                }
            }

            if(hasDigit){
                continue;
            }
            
            //处理标点符号
            //如果单词末尾出现符号，不统计
            if(ispunct(word.back())){
                continue;
            }

            //如果单词开头出现符号，不统计
            if(ispunct(word.front())){
                continue;
            }

            //如果单词首字母大写，转成小写
            if(isupper(word[0])){
                word[0] = tolower(word[0]);
            }

    
            //用下标操作保存单词出现次数
            ++_dict[word];

        #if 0
            //记录单词的行号
            auto it_line = _wordNumbers.find(word);
            if(it_line == _wordNumbers.end()){//没找到
                //给_wordNumber这个map插入一个pair{string,set<int>}
                //新的单词和行号
                set<int> setLine = {lineNumber};
                _wordNumbers.insert({word,setLine});
            }else{//找到了返回相应的位置的迭代器
                //迭代器->second访问保存行号的set
                //再对set去插入行号
                //行号如果存在就插入失败，实现行号只保存一份的效果
                (it_line->second).insert(lineNumber);
            }
        #endif   
            //利用下标访问的特性替代上面的逻辑判断
            //如果找到了word，返回对应的set<int>，
            //对行号集合再去插入行号
            //如果没找到word，会自动给map插入一个pair
            //这个pair是新出现的word和一个空的行号集合组成的
            //最后返回的还是这个空的set
            //再对行号集合插入行号，进行记录
            _wordNumbers[word].insert(lineNumber);
        }
    }
    ifs.close();
}

void TextQuery::query(const string & word){
    auto itDict = _dict.find(word);
    if(itDict == _dict.end()){
        cerr << "The word: "  << word << " not exist!" << endl;
        return;
    }

    cout << "--------------------------" << endl;
    //输出单词出现的次数
    cout << "The word: " << word << " occurs " 
        <<  itDict->second << " times." << endl;

    //输出单词所在的行号，和这一行的内容
    //set<int> Lines = _wordNumber[word];
    //
    //map<string,set<int>>::iterator it_line;
    auto it_line = _wordNumbers.find(word);
    set<int> Lines = it_line->second;
    //用迭代器方式遍历set
    //set<int>::iterator it_set = Lines.begin();
    auto it_set = Lines.begin();
    for(; it_set != Lines.end(); ++it_set){
        int rowNumber = *it_set;
        cout << "(line " << rowNumber << ") ";
        //通过vector的下标取出一行的内容
        cout << _lines[rowNumber - 1] << endl;
    }
    cout << "--------------------------" << endl;

}



//程序测试用例
int main(int argc, char *argv[])
{
    string queryWord("the");

    TextQuery tq;
    tq.readFile("china_daily.txt");
    tq.query(queryWord);
    return 0;
}
